home *** CD-ROM | disk | FTP | other *** search
/ C.A.R.R.S. / C.A.R.R.S. CD-ROM (1990).ISO / 004a / ed.lzh / ED.C next >
C/C++ Source or Header  |  1989-02-15  |  44KB  |  2,223 lines

  1. /*
  2.  *  ed - standard editor
  3.  *  ~~
  4.  *    Authors: Brian Beattie, Kees Bot, and others
  5.  *
  6.  * Copyright 1987 Brian Beattie Rights Reserved.
  7.  * Permission to copy or distribute granted under the following conditions:
  8.  * 1). No charge may be made other than reasonable charges for reproduction.
  9.  * 2). This notice must remain intact.
  10.  * 3). No further restrictions may be added.
  11.  *
  12.  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  13.  *  TurboC mods and cleanup 8/17/88 RAMontante.
  14.  *  Further information (posting headers, etc.) at end of file.
  15.  * _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
  16.  */
  17.  
  18. int    version = 4;    /* used only in the "set" function, for i.d. */
  19.  
  20. #include <stdio.h>
  21.  
  22. #ifdef __TURBOC__
  23. #include <stdlib.h>
  24. #include <signal.h>
  25. #include <string.h>
  26. #include <io.h>
  27.  
  28. #define    _cleanup()    ;
  29.  
  30. #endif  /* ifdef __TURBOC__  */
  31.  
  32.  
  33. /*
  34.  *    #defines for non-printing ASCII characters
  35.  */
  36. #define NUL    0x00    /* ^@ */
  37. #define EOS    0x00    /* end of string */
  38. #define SOH    0x01    /* ^A */
  39. #define STX    0x02    /* ^B */
  40. #define ETX    0x03    /* ^C */
  41. #define EOT    0x04    /* ^D */
  42. #define ENQ    0x05    /* ^E */
  43. #define ACK    0x06    /* ^F */
  44. #define BEL    0x07    /* ^G */
  45. #define BS    0x08    /* ^H */
  46. #define HT    0x09    /* ^I */
  47. #define LF    0x0a    /* ^J */
  48. #define NL    '\n'
  49. #define VT    0x0b    /* ^K */
  50. #define FF    0x0c    /* ^L */
  51. #define CR    0x0d    /* ^M */
  52. #define SO    0x0e    /* ^N */
  53. #define SI    0x0f    /* ^O */
  54. #define DLE    0x10    /* ^P */
  55. #define DC1    0x11    /* ^Q */
  56. #define DC2    0x12    /* ^R */
  57. #define DC3    0x13    /* ^S */
  58. #define DC4    0x14    /* ^T */
  59. #define NAK    0x15    /* ^U */
  60. #define SYN    0x16    /* ^V */
  61. #define ETB    0x17    /* ^W */
  62. #define CAN    0x18    /* ^X */
  63. #define EM    0x19    /* ^Y */
  64. #define SUB    0x1a    /* ^Z */
  65. #define ESC    0x1b    /* ^[ */
  66. #define FS    0x1c    /* ^\ */
  67. #define GS    0x1d    /* ^] */
  68. #define RS    0x1e    /* ^^ */
  69. #define US    0x1f    /* ^_ */
  70. #define SP    0x20    /* space */
  71. #define DEL    0x7f    /* DEL*/
  72.  
  73.  
  74. /*    Definitions of meta-characters used in pattern matching
  75.  *    routines.  LITCHAR & NCCL are only used as token identifiers;
  76.  *    all the others are also both token identifier and actual symbol
  77.  *    used in the regular expression.
  78.  */
  79. #define BOL    '^'
  80. #define EOL    '$'
  81. #define ANY    '.'
  82. #define LITCHAR    'L'
  83. #define    ESCAPE    '\\'
  84. #define CCL    '['    /* Character class: [...] */
  85. #define CCLEND    ']'
  86. #define NEGATE    '~'
  87. #define NCCL    '!'    /* Negative character class [^...] */
  88. #define CLOSURE    '*'
  89. #define OR_SYM    '|'
  90. #define DITTO    '&'
  91. #define OPEN    '('
  92. #define CLOSE    ')'
  93.  
  94.  
  95. /* Largest permitted size for an expanded character class.  (i.e. the class
  96.  * [a-z] will expand into 26 symbols; [a-z0-9] will expand into 36.)
  97.  */
  98. #define CLS_SIZE    128
  99.  
  100.  
  101. #define TRUE    1
  102. #define FALSE    0
  103. #define ERR        -2
  104. #define FATAL        (ERR-1)
  105. #define CHANGED        (ERR-2)
  106. #define SET_FAIL    (ERR-3)
  107. #define SUB_FAIL    (ERR-4)
  108.  
  109.  
  110. #define    BUFFER_SIZE    2048    /* stream-buffer size:  == 1 hd cluster */
  111.  
  112. #define LINFREE    1    /* entry not in use */
  113. #define LGLOB    2       /* line marked global */
  114.  
  115. #define MAXLINE    512    /* max number of chars per line */
  116. #define MAXPAT    256    /* max number of chars per replacement pattern */
  117. #define MAXFNAME 256    /* max file name size */
  118.  
  119.  
  120. /**  Global variables  **/
  121.  
  122. /*  Tokens are used to hold pattern templates. (see makepat())  */
  123.  
  124. typedef    char    BITMAP;
  125. typedef struct token {
  126.     char        tok;
  127.     char        lchar;
  128.     BITMAP        *bitmap;
  129.     struct token    *next;
  130. } TOKEN;
  131.  
  132. #define TOKSIZE sizeof (TOKEN)
  133.  
  134.  
  135. struct    line {
  136.     int        l_stat;        /* empty, mark */
  137.     struct line    *l_prev;
  138.     struct line    *l_next;
  139.     char        l_buff[1];
  140. };
  141. typedef struct line    LINE;
  142.  
  143.  
  144. int    diag = 1;        /* diagnostic-output? flag */
  145. char    *paropen[9];
  146. char    *parclose[9];
  147. int    between, parnum;
  148. int    truncflg = 1;    /* truncate long line flag */
  149. int    eightbit = 1;    /* save eighth bit */
  150. int    nonascii;    /* count of non-ascii chars read */
  151. int    nullchar;    /* count of null chars read */
  152. int    truncated;    /* count of lines truncated */
  153. char    fname[MAXFNAME];
  154. int    fchanged;    /* file-changed? flag */
  155. int    nofname;
  156. int    mark['z'-'a'+1];
  157. TOKEN    *oldpat;
  158.  
  159. LINE    Line0;
  160. int    CurLn = 0;
  161. int    LastLn = 0;
  162. char    inlin[MAXLINE];
  163. int    pflag;
  164. int    Line1, Line2, nlines;
  165. int    nflg;        /* print line number flag */
  166. int    lflg;        /* print line in verbose mode */
  167. int    pflg;        /* print current line after each command */
  168. char    *inptr;        /* tty input buffer */
  169.  
  170. struct tbl {
  171.     char    *t_str;
  172.     int    *t_ptr;
  173.     int    t_val;
  174. } *t, tbl[] = {
  175.     "number",    &nflg,        TRUE,
  176.     "nonumber",    &nflg,        FALSE,
  177.     "list",        &lflg,        TRUE,
  178.     "nolist",    &lflg,        FALSE,
  179.     "eightbit",    &eightbit,    TRUE,
  180.     "noeightbit",    &eightbit,    FALSE,
  181.     0
  182. };
  183.  
  184.  
  185. /*-------------------------------------------------------------------------*/
  186.  
  187. #ifdef __TURBOC__        /*  prototypes (unneeded?)  */
  188.  
  189. void    prntln(char *, int, int);
  190. void    putcntl(char , FILE *);
  191. int    doprnt(int, int);
  192. LINE    *getptr(int);
  193. BITMAP    *makebitmap(unsigned);
  194. void    relink(LINE *, LINE *, LINE *, LINE *);
  195. int    del(int, int);
  196. int    ins(char *);
  197. char    *gettxt(int);
  198. int    append(int, int);
  199. char    *catsub(char *, char *, char *, char *, char *);
  200. char    *matchs(char *, TOKEN *, int);
  201. int    deflt(int, int);
  202. char    *maksub(char *sub, int subsz);
  203. TOKEN    *optpat(void);
  204.  
  205. #else    /* !__TURBOC__ */
  206.  
  207. extern    char    *strcpy();
  208. extern    int    *malloc();
  209. extern    LINE    *getptr();
  210. BITMAP    *makebitmap();
  211. extern    char    *gettxt();
  212. extern    char    *catsub();
  213. extern    char    *matchs();
  214. char    *maksub();
  215. TOKEN    *optpat();
  216.  
  217. #endif  /* __TURBOC__ */
  218.  
  219.  
  220. /*________  Macros  ________________________________________________________*/
  221.  
  222. #ifndef max
  223. #  define max(a,b)    ((a) > (b) ? (a) : (b))
  224. #endif
  225.  
  226. #ifndef min
  227. #  define min(a,b)    ((a) < (b) ? (a) : (b))
  228. #endif
  229.  
  230. #ifndef toupper
  231. #  define toupper(c)    ((c >= 'a' && c <= 'z') ? c-32 : c )
  232. #endif
  233.  
  234. /*  getpat -- Translate arg into a TOKEN string  */
  235. #define    getpat(arg)    makepat((arg), '\000')
  236.  
  237. #define nextln(l)    ((l)+1 > LastLn ? 0 : (l)+1)
  238. #define prevln(l)    ((l)-1 < 0 ? LastLn : (l)-1)
  239.  
  240. #define clrbuf()    del(1, LastLn)
  241.  
  242. #define    Skip_White_Space    {while (*inptr==SP || *inptr==HT) inptr++;}
  243.  
  244.  
  245. /*________  functions  ______________________________________________________*/
  246.  
  247. /*****************************************************************************
  248.  *    BITMAP.C -    makebitmap, setbit, testbit
  249.  *            bit-map manipulation routines.
  250.  *
  251.  *    Copyright (c) Allen I. Holub, all rights reserved.  This program may
  252.  *        for copied for personal, non-profit use only.
  253.  */
  254. BITMAP    *makebitmap( size )
  255. unsigned size;
  256. {
  257.     /*    Make a bit map with "size" bits.  The first entry in
  258.      *    the map is an "unsigned int" representing the maximum
  259.      *    bit.  The map itself is concatenated to this integer.
  260.      *    Return a pointer to a map on success, 0 if there's
  261.      *    not enough memory.
  262.      */
  263.  
  264.     unsigned *map, numbytes;
  265.  
  266.     numbytes = (size >> 3) + ((size & 0x07) ? 1 : 0 );
  267.  
  268. #ifdef DEBUG
  269.     printf("Making a %u bit map (%u bytes required)\n", size, numbytes);
  270. #endif
  271.  
  272.     if( map = (unsigned *) malloc( numbytes + sizeof(unsigned) ))
  273.         *map = size;
  274.  
  275.     return ((BITMAP *)map);
  276. }
  277.  
  278. setbit( c, map, val )
  279. unsigned    c, val;
  280. char        *map;
  281. {
  282.     /*    Set bit c in the map to val.
  283.      *    If c > map-size, 0 is returned, else 1 is returned.
  284.      */
  285.  
  286.     if( c >= *(unsigned *)map )    /* if c >= map size */
  287.         return 0;
  288.  
  289.     map += sizeof(unsigned);    /* skip past size */
  290.  
  291.     if( val )
  292.         map[c >> 3] |= 1 << (c & 0x07);
  293.     else
  294.         map[c >> 3] &= ~(1 << (c & 0x07));
  295.  
  296.     return( 1 );
  297. }
  298.  
  299. testbit( c, map )
  300. unsigned    c;
  301. char        *map;
  302. {
  303.     /*    Return 1 if the bit corresponding to c in map is set.
  304.      *    0 if it is not.
  305.      */
  306.  
  307.     if( c >= *(unsigned *)map )
  308.         return 0;
  309.  
  310.     map += sizeof(unsigned);
  311.  
  312.     return(map[ c >> 3 ] & (1 << (c & 0x07)));
  313. }
  314. /*********  end of BITMAP.C  ************************************************/
  315.  
  316.  
  317. /*    omatch.c
  318.  *
  319.  * Match one pattern element, pointed at by pat, with the character at
  320.  * **linp.  Return non-zero on match.  Otherwise, return 0.  *Linp is
  321.  * advanced to skip over the matched character; it is not advanced on
  322.  * failure.  The amount of advance is 0 for patterns that match null
  323.  * strings, 1 otherwise.  "boln" should point at the position that will
  324.  * match a BOL token.
  325.  */
  326. omatch(linp, pat, boln)
  327. char    **linp;
  328. TOKEN    *pat;
  329. char    *boln;
  330. {
  331.     register int    advance;
  332.  
  333.     advance = -1;
  334.     if (**linp) {
  335.         switch (pat->tok) {
  336.         case LITCHAR:
  337.             if (**linp == pat->lchar)
  338.                 advance = 1;
  339.             break;
  340.  
  341.         case BOL:
  342.             if (*linp == boln)    /* Replaced (*linp = boln */
  343.                 advance = 0;
  344.             break;
  345.  
  346.         case ANY:
  347.             if (**linp != '\n')
  348.                 advance = 1;
  349.             break;
  350.  
  351.         case EOL:
  352.             if (**linp == '\n')
  353.                 advance = 0;
  354.             break;
  355.  
  356.         case CCL:
  357.             if( testbit( **linp, pat->bitmap))
  358.                 advance = 1;
  359.             break;
  360.  
  361.         case NCCL:
  362.             if (!testbit (**linp, pat->bitmap))
  363.                 advance = 1;
  364.             break;
  365.         }
  366.     }
  367.     if (advance >= 0)
  368.         *linp += advance;
  369.  
  370.     return (++advance);
  371. }
  372.  
  373.  
  374. static char *match(lin, pat, boln)
  375. char    *lin;
  376. TOKEN    *pat;
  377. char    *boln;
  378. {
  379.     register char    *bocl, *rval, *strstart;
  380.  
  381.     if(pat == 0)
  382.         return 0;
  383.  
  384.     strstart = lin;
  385.     while(pat) {
  386.         if(pat->tok == CLOSURE && pat->next) {
  387.  
  388.             /* Process a closure:
  389.              * first skip over the closure token to the
  390.              * object to be repeated.  This object can be
  391.              * a character class.
  392.              */
  393.             pat = pat->next;
  394.  
  395.             /* Now match as many occurrences of the
  396.              * closure pattern as possible.
  397.              */
  398.             bocl = lin;
  399.             while( *lin && omatch(&lin, pat))
  400.                 ;
  401.  
  402.             /* 'Lin' now points to the character that
  403.              * made us fail.  Now go on to process the
  404.              * rest of the string.  A problem here is
  405.              * a character following the closure which
  406.              * could have been in the closure.
  407.              * For example, in the pattern "[a-z]*t" (which
  408.              * matches any lower-case word ending in a t),
  409.              * the final 't' will be sucked up in the while
  410.              * loop.  So, if the match fails, we back up a
  411.              * notch and try to match the rest of the
  412.              * string again, repeating this process
  413.              * recursively until we get back to the
  414.              * beginning of the closure.  The recursion
  415.              * goes, at most two levels deep.
  416.              */
  417.             if(pat = pat->next)
  418.             {
  419.                 int savbtwn=between;
  420.                 int savprnm=parnum;
  421.  
  422.                 while(bocl <= lin) {
  423.                     if (rval = match(lin, pat, boln)) {
  424.                         return(rval);    /* success */
  425.                     } else {
  426.                         --lin;
  427.                         between=savbtwn;
  428.                         parnum=savprnm;
  429.                     }
  430.                 }
  431.                 return (0);    /* match failed */
  432.             }
  433.         } else
  434.         if (pat->tok == OPEN) {
  435.             if (between || parnum>=9)
  436.                 return 0;
  437.             paropen[parnum] = lin;
  438.             between = 1;
  439.             pat = pat->next;
  440.         } else
  441.         if (pat->tok == CLOSE) {
  442.             if (!between)
  443.                 return 0;
  444.             parclose[parnum++] = lin;
  445.             between = 0;
  446.             pat = pat->next;
  447.         } else
  448.         if (omatch(&lin, pat, boln)) {
  449.             pat = pat->next;
  450.         } else {
  451.             return (0);
  452.         }
  453.     }
  454.     /* Note that omatch() advances lin to point at the next
  455.      * character to be matched.  Consequently, when we reach
  456.      * the end of the template, lin will be pointing at the
  457.      * character following the last character matched.  The
  458.      * exceptions are templates containing only a BOLN or EOLN
  459.      * token.  In these cases omatch doesn't advance.
  460.      *
  461.      * A philosophical point should be mentioned here.  Is $
  462.      * a position or a character? (i.e. does $ mean the EOL
  463.      * character itself or does it mean the character at the end
  464.      * of the line.)  I decided here to make it mean the former,
  465.      * in order to make the behavior of match() consistent.  If
  466.      * you give match the pattern ^$ (match all lines consisting
  467.      * only of an end of line) then, since something has to be
  468.      * returned, a pointer to the end of line character itself is
  469.      * returned.
  470.      */
  471.  
  472.     return ((char *)max(strstart , lin));
  473. }
  474.  
  475.  
  476. /*  amatch.c
  477.  *
  478.  *    Scans throught the pattern template looking for a match
  479.  *    with lin.  Each element of lin is compared with the template
  480.  *    until either a mis-match is found or the end of the template
  481.  *    is reached.  In the former case a 0 is returned; in the latter,
  482.  *    a pointer into lin (pointing to the character following the
  483.  *    matched pattern) is returned.
  484.  *
  485.  *    "lin"    is a pointer to the line being searched.
  486.  *    "pat"    is a pointer to a template made by makepat().
  487.  *    "boln"    is a pointer into "lin" which points at the
  488.  *            character at the beginning of the line.
  489.  */
  490. char *amatch(lin, pat, boln)
  491. char    *lin;
  492. TOKEN    *pat;
  493. char    *boln;
  494. {
  495.     between = parnum = 0;
  496.     lin = match(lin, pat, boln);
  497.  
  498.     if (between)
  499.         return 0;
  500.  
  501.     while (parnum<9) {
  502.         paropen[parnum] = parclose[parnum] = "";
  503.         parnum++;
  504.     }
  505.     return lin;
  506. }
  507.  
  508.  
  509. /*    append.c    */
  510.  
  511. append(line, glob)
  512. int    line, glob;
  513. {
  514.     int    stat;
  515.     char    lin[MAXLINE];
  516.  
  517.     if(glob)
  518.         return(ERR);
  519.     CurLn = line;
  520.     for (;;) {
  521.         if(nflg)
  522.             printf("%6d. ",CurLn+1);
  523.  
  524.         if(fgets(lin, MAXLINE, stdin) == NULL)
  525.             return( EOF );
  526.         if(lin[0] == '.' && lin[1] == '\n')
  527.             return(0);
  528.         stat = ins(lin);
  529.         if(stat < 0)
  530.             return( ERR );
  531.  
  532.     }
  533. }
  534.  
  535.  
  536. /*    catsub.c    */
  537.  
  538. char *catsub(from, to, sub, new, newend)
  539. char    *from, *to, *sub, *new, *newend;
  540. {
  541.     char    *cp, *cp2;
  542.  
  543.     for(cp = new; *sub != EOS && cp < newend;) {
  544.         if(*sub == DITTO)
  545.             for(cp2 = from; cp2 < to;) {                *cp++ = *cp2++;
  546.                 if(cp >= newend)
  547.                     break;
  548.             }
  549.         else
  550.         if (*sub == ESCAPE) {
  551.             sub++;
  552.             if ('1' <= *sub && *sub <= '9') {
  553.                 char *parcl = parclose[*sub - '1'];
  554.  
  555.                 for (cp2 = paropen[*sub - '1']; cp2 < parcl;) {
  556.                     *cp++ = *cp2++;
  557.                     if (cp >= newend)
  558.                         break;
  559.                 }
  560.             } else
  561.                 *cp++ = *sub;
  562.         } else
  563.             *cp++ = *sub;
  564.  
  565.         sub++;
  566.     }
  567.  
  568.     return(cp);
  569. }
  570.  
  571.  
  572. /*    ckglob.c    */
  573.  
  574. ckglob()
  575. {
  576.     TOKEN    *glbpat;
  577.     char    c, delim, *lin;
  578.     int    num;
  579.     LINE    *ptr;
  580.  
  581.     c = *inptr;
  582.  
  583.     if(c != 'g' && c != 'v')
  584.         return(0);
  585.     if (deflt(1, LastLn) < 0)
  586.         return(ERR);
  587.  
  588.     delim = *++inptr;
  589.     if(delim <= ' ')
  590.         return(ERR);
  591.  
  592.     glbpat = optpat();
  593.     if(*inptr == delim)
  594.         inptr++;
  595.     for (num=1; num<=LastLn; num++) {
  596.         ptr = getptr(num);
  597.         ptr->l_stat &= ~LGLOB;
  598.         if (Line1 <= num && num <= Line2) {
  599.             lin = gettxt(num);
  600.             if(matchs(lin, glbpat, 0)) {
  601.                 if (c=='g') ptr->l_stat |= LGLOB;
  602.             } else {
  603.                 if (c=='v') ptr->l_stat |= LGLOB;
  604.             }
  605.         }
  606.     }
  607.     return(1);
  608. }
  609.  
  610.  
  611. /*  deflt.c
  612.  *    Set Line1 & Line2 (the command-range delimiters) if the file is
  613.  *    empty; Test whether they have valid values.
  614.  */
  615.  
  616. int deflt(def1, def2)
  617. int    def1, def2;
  618. {
  619.     if(nlines == 0) {
  620.         Line1 = def1;
  621.         Line2 = def2;
  622.     }
  623.     return ( (Line1>Line2 || Line1<=0) ? ERR : 0 );
  624. }
  625.  
  626.  
  627. /*    del.c    */
  628.  
  629. /* One of the calls to this function tests its return value for an error
  630.  * condition.  But del doesn't return any error value, and it isn't obvious
  631.  * to me what errors might be detectable/reportable.  To silence a warning
  632.  * message, I've added a constant return statement. -- RAM
  633.  */
  634.  
  635. del(from, to)
  636. int    from, to;
  637. {
  638.     LINE    *first, *last, *next, *tmp;
  639.  
  640.     if(from < 1)
  641.         from = 1;
  642.     first = getptr(prevln(from));
  643.     last = getptr(nextln(to));
  644.     next = first->l_next;
  645.     while(next != last && next != &Line0) {
  646.         tmp = next->l_next;
  647.         free(next);
  648.         next = tmp;
  649.     }
  650.     relink(first, last, first, last);
  651.     LastLn -= (to - from)+1;
  652.     CurLn = prevln(from);
  653.     return(0);
  654. }
  655.  
  656.  
  657. int dolst(line1, line2)
  658. int line1, line2;
  659. {
  660.     int oldlflg=lflg, p;
  661.  
  662.     lflg = 1;
  663.     p = doprnt(line1, line2);
  664.     lflg = oldlflg;
  665.     return p;
  666. }
  667.  
  668.  
  669. /*    esc.c
  670.  * Map escape sequences into their equivalent symbols.  Returns the
  671.  * correct ASCII character.  If no escape prefix is present then s
  672.  * is untouched and *s is returned, otherwise **s is advanced to point
  673.  * at the escaped character and the translated character is returned.
  674.  */
  675. esc(s)
  676. char    **s;
  677. {
  678.     register int    rval;
  679.  
  680.     if (**s != ESCAPE) {
  681.         rval = **s;
  682.     } else {
  683.         (*s)++;
  684.         switch(toupper(**s)) {
  685.         case '\000':
  686.             rval = ESCAPE;    break;
  687.         case 'S':
  688.             rval = ' ';    break;
  689.         case 'N':
  690.             rval = '\n';    break;
  691.         case 'T':
  692.             rval = '\t';    break;
  693.         case 'B':
  694.             rval = '\b';    break;
  695.         case 'R':
  696.             rval = '\r';    break;
  697.         default:
  698.             rval = **s;    break;
  699.         }
  700.     }
  701.     return (rval);
  702. }
  703.  
  704.  
  705. /*  dodash.c
  706.  *    Expand the set pointed to by *src into dest.
  707.  *    Stop at delim.  Return 0 on error or size of
  708.  *    character class on success.  Update *src to
  709.  *    point at delim.  A set can have one element
  710.  *    {x} or several elements ( {abcdefghijklmnopqrstuvwxyz}
  711.  *    and {a-z} are equivalent ).  Note that the dash
  712.  *    notation is expanded as sequential numbers.
  713.  *    This means (since we are using the ASCII character
  714.  *    set) that a-Z will contain the entire alphabet
  715.  *    plus the symbols: [\]^_`.  The maximum number of
  716.  *    characters in a character class is defined by maxccl.
  717.  */
  718. char *dodash(delim, src, map)
  719. int    delim;
  720. char    *src, *map;
  721. {
  722.  
  723.     register int    first,    last;
  724.     char        *start;
  725.  
  726.     start = src;
  727.  
  728.     while( *src && *src != delim ) {
  729.         if( *src != '-')
  730.             setbit( esc( &src ), map, 1 );
  731.  
  732.         else
  733.         if( src == start || *(src + 1) == delim )
  734.             setbit( '-', map, 1 );
  735.         else {
  736.             src++;
  737.             if( *src < *(src - 2)) {
  738.                 first = *src;
  739.                 last = *(src - 2);
  740.             } else {
  741.                 first = *(src - 2);
  742.                 last = *src;
  743.             }
  744.             while( ++first <= last )
  745.                 setbit( first, map, 1);
  746.         }
  747.         src++;
  748.     }
  749.     return( src );
  750. }
  751.  
  752.  
  753. /*    doprnt.c    */
  754.  
  755. int doprnt(from, to)
  756. int    from, to;
  757. {
  758.     from = (from < 1) ? 1 : from;
  759.     to = (to > LastLn) ? LastLn : to;
  760.  
  761.     if(to != 0) {
  762.         for(CurLn = from; CurLn <= to; CurLn++)
  763.             prntln(gettxt(CurLn), lflg, (nflg ? CurLn : 0));
  764.         CurLn = to;
  765.     }
  766.  
  767.     return(0);
  768. }
  769.  
  770.  
  771. void prntln(str, vflg, lin)
  772. char    *str;
  773. int    vflg, lin;
  774. {
  775.     if(lin)
  776.         printf("%7d ",lin);
  777.     while(*str && *str != NL) {
  778.         if(*str < ' ' || *str >= 0x7f) {
  779.             switch(*str) {
  780.             case '\t':
  781.                 if(vflg)
  782.                     putcntl(*str, stdout);
  783.                 else
  784.                     putc(*str, stdout);
  785.                 break;
  786.  
  787.             case DEL:
  788.                 putc('^', stdout);
  789.                 putc('?', stdout);
  790.                 break;
  791.  
  792.             default:
  793.                 putcntl(*str, stdout);
  794.                 break;
  795.             }
  796.         } else
  797.             putc(*str, stdout);
  798.         str++;
  799.     }
  800.     if(vflg)
  801.         putchar('$');
  802.     putchar('\n');
  803. }
  804.  
  805.  
  806. void putcntl(c, stream)
  807. char    c;
  808. FILE    *stream;
  809. {
  810.     putc('^', stream);
  811.     putc((c&31)|'@', stream);
  812. }
  813.  
  814.  
  815. /*    egets.c    */
  816.  
  817. egets(str,size,stream)
  818. char    *str;
  819. int    size;
  820. FILE    *stream;
  821. {
  822.     int    c, count;
  823.     char    *cp;
  824.  
  825.     for(count = 0, cp = str; size > count;) {
  826.         c = getc(stream);
  827.         if(c == EOF) {
  828.             *cp++ = '\n';
  829.             *cp = EOS;
  830.             if(count)
  831.                 puts("[Incomplete last line]");
  832.             return(count);
  833.         }
  834.         if(c == NL) {
  835.             *cp++ = c;
  836.             *cp = EOS;
  837.             return(++count);
  838.         }
  839.         if(c > 127) {
  840.             if(!eightbit)        /* if not saving eighth bit */
  841.                 c = c&127;    /* strip eigth bit */
  842.             nonascii++;        /* count it */
  843.         }
  844.         if(c) {
  845.             *cp++ = c;    /* not null, keep it */
  846.             count++;
  847.         } else
  848.             nullchar++;    /* count nulls */
  849.     }
  850.     str[count-1] = EOS;
  851.     if(c != NL) {
  852.         puts("truncating line");
  853.         truncated++;
  854.         while((c = getc(stream)) != EOF)
  855.             if(c == NL)
  856.                 break;
  857.     }
  858.     return(count);
  859. }  /* egets */
  860.  
  861.  
  862. doread(lin, fname)
  863. int    lin;
  864. char    *fname;
  865. {
  866.     FILE    *fp;
  867.     int    err;
  868.     unsigned long    bytes;
  869.     unsigned int    lines;
  870.     static char    str[MAXLINE];
  871.  
  872.     err = 0;
  873.     nonascii = nullchar = truncated = 0;
  874.  
  875.     if (diag) printf("\"%s\" ",fname);
  876.     if( (fp = fopen(fname, "r")) == NULL ) {
  877.         puts(" isn't readable.");
  878.         return( ERR );
  879.     }
  880.     setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE);
  881.     CurLn = lin;
  882.     for(lines = 0, bytes = 0;(err = egets(str,MAXLINE,fp)) > 0;) {
  883.         bytes += strlen(str);
  884.         if(ins(str) < 0) {
  885.             puts("file insert error");
  886.             err++;
  887.             break;
  888.         }
  889.         lines++;
  890.     }
  891.     fclose(fp);
  892.     if(err < 0)
  893.         return(err);
  894.     if (diag) {
  895.         printf("%u lines %u bytes",lines,bytes);
  896.         if(nonascii)
  897.             printf(" [%d non-ascii]",nonascii);
  898.         if(nullchar)
  899.             printf(" [%d nul]",nullchar);
  900.         if(truncated)
  901.             printf(" [%d lines truncated]",truncated);
  902.         putchar('\n');
  903.     }
  904.     return( err );
  905. }  /* doread */
  906.  
  907.  
  908. int dowrite(from, to, fname, apflg)
  909. int    from, to;
  910. char    *fname;
  911. int    apflg;
  912. {
  913.     FILE    *fp;
  914.     int    lin, err;
  915.     unsigned int    lines, bytes;
  916.     char    *str;
  917.     LINE    *lptr;
  918.  
  919.     err = 0;
  920.     lines = bytes = 0;
  921.  
  922.     printf("\"%s\" ",fname);
  923.     if((fp = fopen(fname,(apflg?"a":"w"))) == NULL) {
  924.         puts(" can't be opened for writing!");
  925.         return( ERR );
  926.     }
  927.  
  928.     setvbuf(fp, NULL, _IOFBF, BUFFER_SIZE);
  929.     lptr = getptr(from);
  930.     for(lin = from; lin <= to; lin++) {
  931.         str = lptr->l_buff;
  932.         lines++;
  933.         bytes += strlen(str) + 1;    /* str + '\n' */
  934.         if(fputs(str, fp) == EOF) {
  935.             puts("file write error");
  936.             err++;
  937.             break;
  938.         }
  939.         fputc('\n', fp);
  940.         lptr = lptr->l_next;
  941.     }
  942.     printf("%u lines %u bytes\n",lines,bytes);
  943.     fclose(fp);
  944.     return( err );
  945. }  /* dowrite */
  946.  
  947.  
  948. /*    find.c    */
  949.  
  950. find(pat, dir)
  951. TOKEN    *pat;
  952. int    dir;
  953. {
  954.     int    i, num;
  955.     char    *lin;
  956.  
  957.     num=CurLn;
  958.     for(i=0; i<LastLn; i++) {
  959.         lin = gettxt(num);
  960.         if(matchs(lin, pat, 0)) {
  961.             return(num);
  962.         }
  963.         num = (dir ? nextln(num) : prevln(num));
  964.     }
  965.     return ( ERR );
  966. }
  967.  
  968.  
  969. /*    getfn.c    */
  970.  
  971. char *getfn()
  972. {
  973.     static char    file[256];
  974.     char    *cp;
  975.  
  976.     if(*inptr == NL) {
  977.         nofname=TRUE;
  978.         strcpy(file, fname);
  979.     } else {
  980.         nofname=FALSE;
  981.         Skip_White_Space;
  982.  
  983.         cp = file;
  984.         while(*inptr && *inptr != NL && *inptr != SP && *inptr != HT)
  985.             *cp++ = *inptr++;
  986.         *cp = '\0';
  987.  
  988.         if(strlen(file) == 0) {
  989.             puts("bad file name");
  990.             return( NULL );
  991.         }
  992.     }
  993.  
  994.     if(strlen(file) == 0) {
  995.         puts("no file name");
  996.         return(NULL);
  997.     }
  998.     return( file );
  999. }  /* getfn */
  1000.  
  1001.  
  1002. int getnum(first)
  1003. int first;
  1004. {
  1005.     TOKEN    *srchpat;
  1006.     int    num;
  1007.     char    c;
  1008.  
  1009.     Skip_White_Space;
  1010.  
  1011.     if(*inptr >= '0' && *inptr <= '9') {    /* line number */
  1012.         for(num = 0; *inptr >= '0' && *inptr <= '9'; ++inptr) {
  1013.             num = (num * 10) + (*inptr - '0');
  1014.         }
  1015.         return num;
  1016.     }
  1017.  
  1018.     switch(c = *inptr) {
  1019.     case '.':
  1020.         inptr++;
  1021.         return (CurLn);
  1022.  
  1023.     case '$':
  1024.         inptr++;
  1025.         return (LastLn);
  1026.  
  1027.     case '/':
  1028.     case '?':
  1029.         srchpat = optpat();
  1030.         if(*inptr == c)
  1031.             *inptr++;
  1032.         return(find(srchpat,c == '/'?1:0));
  1033.  
  1034.     case '-':
  1035.     case '+':
  1036.         return(first ? CurLn : 1);
  1037.  
  1038.     case '\'':
  1039.         inptr++;
  1040.         if (*inptr < 'a' || *inptr > 'z')
  1041.             return(EOF);
  1042.         return mark[ *inptr++ - 'a' ];
  1043.  
  1044.     default:
  1045.         return ( first ? EOF : 1 );    /* unknown address */
  1046.     }
  1047. }  /* getnum */
  1048.  
  1049.  
  1050. /*  getone.c
  1051.  *    Parse a number (or arithmetic expression) off the command line.
  1052.  */
  1053. #define FIRST 1
  1054. #define NOTFIRST 0
  1055.  
  1056. int getone()
  1057. {
  1058.     int    c, i, num;
  1059.  
  1060.     if((num = getnum(FIRST)) >= 0) {
  1061.         for (;;) {
  1062.             Skip_White_Space;
  1063.             if(*inptr != '+' && *inptr != '-')
  1064.                 break;    /* exit infinite loop */
  1065.  
  1066.                         c = *inptr++;
  1067.             if((i = getnum(NOTFIRST)) < 0)
  1068.                 return ( i );
  1069.             if(c == '+')
  1070.                 num += i;
  1071.             else
  1072.                 num -= i;
  1073.         }
  1074.     }
  1075.     return ( num>LastLn ? ERR : num );
  1076. }  /* getone */
  1077.  
  1078.  
  1079. getlst()
  1080. {
  1081.     int    num;
  1082.  
  1083.     Line2 = 0;
  1084.     for(nlines = 0; (num = getone()) >= 0;)
  1085.     {
  1086.         Line1 = Line2;
  1087.         Line2 = num;
  1088.         nlines++;
  1089.         if(*inptr != ',' && *inptr != ';')
  1090.             break;
  1091.         if(*inptr == ';')
  1092.             CurLn = num;
  1093.         inptr++;
  1094.     }
  1095.     nlines = min(nlines, 2);
  1096.     if(nlines == 0)
  1097.         Line2 = CurLn;
  1098.     if(nlines <= 1)
  1099.         Line1 = Line2;
  1100.  
  1101.     return ( (num == ERR) ? num : nlines );
  1102. }  /* getlst */
  1103.  
  1104.  
  1105. /*    getptr.c    */
  1106.  
  1107. LINE *getptr(num)
  1108. int    num;
  1109. {
  1110.     LINE    *ptr;
  1111.     int    j;
  1112.  
  1113.     if (2*num>LastLn && num<=LastLn) {    /* high line numbers */
  1114.         ptr = Line0.l_prev;
  1115.         for (j = LastLn; j>num; j--)
  1116.             ptr = ptr->l_prev;
  1117.     } else {                /* low line numbers */
  1118.         ptr = &Line0;
  1119.         for(j = 0; j < num; j++)
  1120.             ptr = ptr->l_next;
  1121.     }
  1122.     return(ptr);
  1123. }
  1124.  
  1125.  
  1126. /*    getrhs.c    */
  1127.  
  1128. getrhs(sub)
  1129. char    *sub;
  1130. {
  1131.     if(inptr[0] == NL || inptr[1] == NL)    /* check for eol */
  1132.         return( ERR );
  1133.     if(maksub(sub, MAXPAT) == NULL)
  1134.         return( ERR );
  1135.  
  1136.     inptr++;        /* skip over delimter */
  1137.     Skip_White_Space;
  1138.     if(*inptr == 'g') {
  1139.         *inptr++;
  1140.         return( 1 );
  1141.     }
  1142.     return( 0 );
  1143. }
  1144.  
  1145.  
  1146. /*    gettxt.c    */
  1147. char *gettxt(num)
  1148. int    num;
  1149. {
  1150.     LINE    *lin;
  1151.     static char    txtbuf[MAXLINE];
  1152.  
  1153.     lin = getptr(num);
  1154.     strcpy(txtbuf,lin->l_buff);
  1155.     strcat(txtbuf,"\n");
  1156.     return(txtbuf);
  1157. }
  1158.  
  1159.  
  1160. /*    ins.c    */
  1161.  
  1162. ins(str)
  1163. char    *str;
  1164. {
  1165.     char    buf[MAXLINE], *cp;
  1166.     LINE    *new, *cur, *nxt;
  1167.  
  1168.     cp = buf;
  1169.     for (;;) {
  1170.         if((*cp = *str++) == NL)
  1171.             *cp = EOS;
  1172.         if(*cp) {
  1173.             cp++;
  1174.             continue;
  1175.         }
  1176.         if((new = (LINE *)malloc(sizeof(LINE)+strlen(buf))) == NULL)
  1177.             return( ERR );     /* no memory */
  1178.  
  1179.         new->l_stat=0;
  1180.         strcpy(new->l_buff,buf);    /* build new line */
  1181.         cur = getptr(CurLn);        /* get current line */
  1182.         nxt = getptr(nextln(CurLn));    /* get next line */
  1183.         relink(cur, new, new, nxt);    /* add to linked list */
  1184.         relink(new, nxt, cur, new);
  1185.         LastLn++;
  1186.         CurLn++;
  1187.  
  1188.         if(*str == EOS)        /* end of line ? */
  1189.             return( 1 );
  1190.  
  1191.         cp = buf;
  1192.     }
  1193. }
  1194.  
  1195.  
  1196. /*    join.c    */
  1197.  
  1198. int join(first, last)
  1199. int first, last;
  1200. {
  1201.     char buf[MAXLINE];
  1202.     char *cp=buf, *str;
  1203.     int num;
  1204.  
  1205.     if (first<=0 || first>last || last>LastLn)
  1206.         return(ERR);
  1207.     if (first==last) {
  1208.         CurLn=first;
  1209.         return 0;
  1210.     }
  1211.     for (num=first; num<=last; num++) {
  1212.         str=gettxt(num);
  1213.         while (*str!=NL && cp<buf+MAXLINE-1)
  1214.             *cp++ = *str++;
  1215.         if (cp==buf+MAXLINE-1) {
  1216.             puts("line too long");
  1217.             return(ERR);
  1218.         }
  1219.     }
  1220.     *cp++ = NL;
  1221.     *cp = EOS;
  1222.     del(first, last);
  1223.     CurLn=first-1;
  1224.     ins(buf);
  1225.     fchanged = TRUE;
  1226.     return 0;
  1227. }
  1228.  
  1229.  
  1230. /*    unmakepat.c
  1231.  * Free up the memory used for token string
  1232.  */
  1233. void unmakepat(head)
  1234. TOKEN    *head;
  1235. {
  1236.     register TOKEN    *old_head;
  1237.  
  1238.     while (head) {
  1239.         switch (head->tok) {
  1240.         case CCL:
  1241.         case NCCL:
  1242.             free(head->bitmap);
  1243.                 /* fall through to default */
  1244.         default:
  1245.             old_head = head;
  1246.             head = head->next;
  1247.             free (old_head);
  1248.             break;
  1249.         }
  1250.     }
  1251. }  /* unmakepat */
  1252.  
  1253.  
  1254. /*    makepat.c
  1255.  *
  1256.  * Make a pattern template from the strinng pointed to by arg.  Stop
  1257.  * when delim or '\000' or '\n' is found in arg.  Return a pointer to
  1258.  * the pattern template.
  1259.  *
  1260.  * The pattern template used here are somewhat different than those
  1261.  * used in the "Software Tools" book; each token is a structure of
  1262.  * the form TOKEN (see tools.h).  A token consists of an identifier,
  1263.  * a pointer to a string, a literal character and a pointer to another
  1264.  * token.  This last is 0 if there is no subsequent token.
  1265.  *
  1266.  * The one strangeness here is caused (again) by CLOSURE which has
  1267.  * to be put in front of the previous token.  To make this insertion a
  1268.  * little easier, the 'next' field of the last to point at the chain
  1269.  * (the one pointed to by 'tail) is made to point at the previous node.
  1270.  * When we are finished, tail->next is set to 0.
  1271.  */
  1272. TOKEN *makepat(arg, delim)
  1273. char    *arg;
  1274. int    delim;
  1275. {
  1276.      TOKEN    *head, *tail, *ntok;
  1277.      int    error;
  1278.  
  1279.     /*
  1280.      * Check for characters that aren't legal at the beginning of
  1281.      * a template.
  1282.      */
  1283.  
  1284.     if (*arg=='\0' || *arg==delim || *arg=='\n' || *arg==CLOSURE)
  1285.         return(0);
  1286.  
  1287.     error = 0;
  1288.     tail = head = NULL;
  1289.  
  1290.     while (*arg && *arg != delim && *arg != '\n' && !error) {
  1291.         ntok = (TOKEN *)malloc(TOKSIZE);
  1292.         ntok->lchar = '\000';
  1293.         ntok->next = 0;
  1294.  
  1295.         switch(*arg) {
  1296.         case ANY:
  1297.             ntok->tok = ANY;
  1298.             break;
  1299.  
  1300.         case BOL:
  1301.             if (head == 0)    /* then this is the first symbol */
  1302.                 ntok->tok = BOL;
  1303.             else {
  1304.                 ntok->tok = LITCHAR;
  1305.                 ntok->lchar = BOL;
  1306.             }
  1307.             break;
  1308.  
  1309.         case EOL:
  1310.             if (*(arg+1) == delim || *(arg+1) == '\000'
  1311.                 || *(arg+1) == '\n')
  1312.             {
  1313.                 ntok->tok = EOL;
  1314.             } else {
  1315.                 ntok->tok = LITCHAR;
  1316.                 ntok->lchar = EOL;
  1317.             }
  1318.             break;
  1319.  
  1320.         case CLOSURE:
  1321.             if (head != 0) {
  1322.                 switch (tail->tok) {
  1323.                 case BOL:
  1324.                 case EOL:
  1325.                 case CLOSURE:
  1326.                     return (0);
  1327.                 default:
  1328.                     ntok->tok = CLOSURE;
  1329.                 }
  1330.             }
  1331.             break;
  1332.  
  1333.         case CCL:
  1334.             if(*(arg + 1) == NEGATE) {
  1335.                 ntok->tok = NCCL;
  1336.                 arg += 2;
  1337.             } else {
  1338.                 ntok->tok = CCL;
  1339.                 arg++;
  1340.             }
  1341.  
  1342.             if( ntok->bitmap = makebitmap(CLS_SIZE) )
  1343.                 arg = dodash(CCLEND, arg, ntok->bitmap );
  1344.             else {
  1345.                 fprintf(stderr,"Not enough memory for pat\n");
  1346.                 error = 1;
  1347.             }
  1348.             break;
  1349.  
  1350.         default:
  1351.             if (*arg == ESCAPE && *(arg+1) == OPEN) {
  1352.                 ntok->tok = OPEN;
  1353.                 arg++;
  1354.             } else
  1355.             if (*arg == ESCAPE && *(arg+1) == CLOSE) {
  1356.                 ntok->tok = CLOSE;
  1357.                 arg++;
  1358.             } else {
  1359.                 ntok->tok = LITCHAR;
  1360.                 ntok->lchar = esc(&arg);
  1361.             }
  1362.         }
  1363.  
  1364.         if (error || ntok == 0) {
  1365.             unmakepat(head);
  1366.             return (0);
  1367.         } else
  1368.         if (head == 0) {    /* This is 1st node in the chain. */
  1369.             ntok->next = 0;
  1370.             head = tail = ntok;
  1371.         } else
  1372.         if (ntok->tok != CLOSURE) {
  1373.             /* Insert at end of list (after tail) */
  1374.             tail->next = ntok;
  1375.             ntok->next = tail;
  1376.             tail = ntok;
  1377.         } else
  1378.         if (head != tail) {
  1379.             /*
  1380.              * More than one node in the chain.  Insert the
  1381.              * CLOSURE node immediately in front of tail.
  1382.              */
  1383.             (tail->next)->next = ntok;
  1384.             ntok->next = tail;
  1385.         } else {
  1386.             /*
  1387.              * Only one node in the chain,  Insert the CLOSURE
  1388.              * node at the head of the linked list.
  1389.              */
  1390.             ntok->next = head;
  1391.             tail->next = ntok;
  1392.             head = ntok;
  1393.         }
  1394.         arg++;
  1395.     }
  1396.     tail->next = 0;
  1397.     return (head);
  1398. }
  1399.  
  1400.  
  1401. /*    maksub.c    */
  1402.  
  1403. char *maksub(char *sub, int subsz)
  1404. {
  1405.     int    size;
  1406.     char    delim, *cp;
  1407.  
  1408.     size = 0;
  1409.     cp = sub;
  1410.  
  1411.     delim = *inptr++;
  1412.     for(size = 0;
  1413.         *inptr != delim && *inptr != NL && size < subsz;
  1414.         size++)
  1415.     {
  1416.         if(*inptr == '&') {
  1417.             *cp++ = DITTO;
  1418.             inptr++;
  1419.         } else
  1420.         if((*cp++ = *inptr++) == ESCAPE) {
  1421.             if (size>=subsz)
  1422.                 return(NULL);
  1423.  
  1424.             switch(toupper(*inptr)) {
  1425.             case NL:
  1426.                 *cp++ == ESCAPE;
  1427.                 break;
  1428.             case 'S':
  1429.                 *cp++ = SP;
  1430.                 inptr++;
  1431.                 break;
  1432.             case 'N':
  1433.                 *cp++ = NL;
  1434.                 inptr++;
  1435.                 break;
  1436.             case 'T':
  1437.                 *cp++ = HT;
  1438.                 inptr++;
  1439.                 break;
  1440.             case 'B':
  1441.                 *cp++ = BS;
  1442.                 inptr++;
  1443.                 break;
  1444.             case 'R':
  1445.                 *cp++ = CR;
  1446.                 inptr++;
  1447.                 break;
  1448.             case '0': {
  1449.                 int i=3;
  1450.                 *cp = 0;
  1451.                 do {
  1452.                     if (*++inptr<'0' || *inptr >'7')
  1453.                         break;
  1454.                     *cp = (*cp<<3) | (*inptr-'0');
  1455.                 } while (--i!=0);
  1456.                 cp++;
  1457.                 } break;
  1458.             default:
  1459.                 *cp++ = *inptr++;
  1460.                 break;
  1461.             }
  1462.         }
  1463.     }
  1464.     if(size >= subsz)
  1465.         return( NULL );
  1466.  
  1467.     *cp = EOS;
  1468.     return( sub );
  1469. }
  1470.  
  1471.  
  1472. /*    matchs.c
  1473.  *
  1474.  * Compares line and pattern.  Line is a character string while pat
  1475.  * is a pattern template made by getpat().
  1476.  * Returns:
  1477.  *    1. A zero if no match was found.
  1478.  *
  1479.  *    2. A pointer to the last character satisfing the match
  1480.  *       if ret_endp is non-zero.
  1481.  *
  1482.  *    3. A pointer to the beginning of the matched string if
  1483.  *       ret_endp is zero.
  1484.  *
  1485.  * e.g.:
  1486.  *
  1487.  *    matchs ("1234567890", getpat("4[0-9]*7), 0);
  1488.  * will return a pointer to the '4', while:
  1489.  *
  1490.  *    matchs ("1234567890", getpat("4[0-9]*7), 1);
  1491.  * will return a pointer to the '7'.
  1492.  */
  1493. char *matchs(line, pat, ret_endp)
  1494. char    *line;
  1495. TOKEN    *pat;
  1496. int    ret_endp;
  1497. {
  1498.     char    *rval, *bptr;
  1499.  
  1500.     bptr = line;
  1501.  
  1502.     while(*line) {
  1503.         if ((rval = amatch(line, pat, bptr)) == 0)
  1504.             line++;
  1505.         else {
  1506.             if(rval > bptr && rval > line)
  1507.                 rval--;        /* point to last char matched */
  1508.             rval = ret_endp ? rval : line;
  1509.             break;
  1510.         }
  1511.     }
  1512.     return (rval);
  1513. }
  1514.  
  1515.  
  1516. /*  move.c
  1517.  *    Unlink the block of lines from Line1 to Line2, and relink them
  1518.  *    after line "num".
  1519.  */
  1520.  
  1521. int move(num)
  1522. int    num;
  1523. {
  1524.     int    range;
  1525.     LINE    *before, *first, *last, *after;
  1526.  
  1527.     if( Line1 <= num && num <= Line2 )
  1528.         return( ERR );
  1529.     range = Line2 - Line1 + 1;
  1530.     before = getptr(prevln(Line1));
  1531.     first = getptr(Line1);
  1532.     last = getptr(Line2);
  1533.     after = getptr(nextln(Line2));
  1534.  
  1535.     relink(before, after, before, after);
  1536.     LastLn -= range;    /* per AST's posted patch 2/2/88 */
  1537.     if (num > Line1)
  1538.         num -= range;
  1539.  
  1540.     before = getptr(num);
  1541.     after = getptr(nextln(num));
  1542.     relink(before, first, last, after);
  1543.     relink(last, after, before, first);
  1544.     LastLn += range;    /* per AST's posted patch 2/2/88 */
  1545.     CurLn = num + range;
  1546.     return( 1 );
  1547. }
  1548.  
  1549.  
  1550. int transfer(num)
  1551. int num;
  1552. {
  1553.     int mid, lin, ntrans;
  1554.  
  1555.     if (Line1<=0 || Line1>Line2)
  1556.         return(ERR);
  1557.  
  1558.     mid= num<Line2 ? num : Line2;
  1559.  
  1560.     CurLn=num;
  1561.     ntrans=0;
  1562.  
  1563.     for (lin=Line1; lin<=mid; lin++) {
  1564.         ins(gettxt(lin));
  1565.         ntrans++;
  1566.     }
  1567.     lin+=ntrans;
  1568.     Line2+=ntrans;
  1569.  
  1570.     for ( ; lin <= Line2; lin += 2 ) {
  1571.         ins(gettxt(lin));
  1572.         Line2++;
  1573.     }
  1574.     return(1);
  1575. }
  1576.  
  1577.  
  1578. /*    optpat.c    */
  1579.  
  1580. TOKEN *optpat(void)
  1581. {
  1582.     char    delim, str[MAXPAT], *cp;
  1583.  
  1584.     delim = *inptr++;
  1585.     cp = str;
  1586.     while(*inptr != delim && *inptr != NL) {
  1587.         if(*inptr == ESCAPE && inptr[1] != NL)
  1588.             *cp++ = *inptr++;
  1589.         *cp++ = *inptr++;
  1590.     }
  1591.  
  1592.     *cp = EOS;
  1593.     if(*str == EOS)
  1594.         return(oldpat);
  1595.     if(oldpat)
  1596.         unmakepat(oldpat);
  1597.     oldpat = getpat(str);
  1598.     return(oldpat);
  1599. }
  1600.  
  1601.  
  1602. set()
  1603. {
  1604.     char    word[16];
  1605.     int    i;
  1606.  
  1607.     if(*(++inptr) != 't') {
  1608.         if(*inptr != SP && *inptr != HT && *inptr != NL)
  1609.             return(ERR);
  1610.     } else
  1611.         inptr++;
  1612.  
  1613.     if ( (*inptr == NL)
  1614. #ifdef __TURBOC__
  1615.         || (*inptr == CR)
  1616. #endif
  1617.        )
  1618.     {
  1619.         printf("ed version %d.%d\n", version/100, version%100);
  1620.         printf(    "number %s, list %s\n",
  1621.             nflg?"ON":"OFF",
  1622.             lflg?"ON":"OFF");
  1623.         return(0);
  1624.     }
  1625.  
  1626.     Skip_White_Space;
  1627.     for(i = 0; *inptr != SP && *inptr != HT && *inptr != NL;)
  1628.         word[i++] = *inptr++;
  1629.     word[i] = EOS;
  1630.     for(t = tbl; t->t_str; t++) {
  1631.         if(strcmp(word,t->t_str) == 0) {
  1632.             *t->t_ptr = t->t_val;
  1633.             return(0);
  1634.         }
  1635.     }
  1636.     return SET_FAIL;
  1637. }
  1638.  
  1639.  
  1640. void relink(a, x, y, b)
  1641. LINE    *a, *x, *y, *b;
  1642. {
  1643.     x->l_prev = a;
  1644.     y->l_next = b;
  1645. }
  1646.  
  1647.  
  1648. void set_ed_buf(void)
  1649. {
  1650.     relink(&Line0, &Line0, &Line0, &Line0);
  1651.     CurLn = LastLn = 0;
  1652. }
  1653.  
  1654.  
  1655. /*    subst.c    */
  1656.  
  1657. subst(pat, sub, gflg, pflag)
  1658. TOKEN    *pat;
  1659. char    *sub;
  1660. int    gflg, pflag;
  1661. {
  1662.     int    lin, chngd, nchngd;
  1663.     char    *txtptr, *txt;
  1664.     char    *lastm, *m, *new, buf[MAXLINE];
  1665.  
  1666.     if(Line1 <= 0)
  1667.         return( SUB_FAIL );
  1668.     nchngd = 0;        /* reset count of lines changed */
  1669.     for(lin = Line1; lin <= Line2; lin++) {
  1670.         txt = txtptr = gettxt(lin);
  1671.         new = buf;
  1672.         chngd = 0;
  1673.         lastm = NULL;
  1674.         while(*txtptr) {
  1675.             if(gflg || !chngd)
  1676.                 m = amatch(txtptr, pat, txt);
  1677.             else
  1678.                 m = NULL;
  1679.             if(m != NULL && lastm != m) {
  1680.                 chngd++;
  1681.                 new = catsub(txtptr, m, sub, new, buf+MAXLINE);
  1682.                 lastm = m;
  1683.             }
  1684.             if(m == NULL || m == txtptr) {
  1685.                 *new++ = *txtptr++;
  1686.             } else {
  1687.                 txtptr = m;
  1688.             }
  1689.         }
  1690.         if(chngd) {
  1691.             if(new >= buf+MAXLINE)
  1692.                 return( SUB_FAIL );
  1693.             *new++ = EOS;
  1694.             del(lin,lin);
  1695.             ins(buf);
  1696.             nchngd++;
  1697.             if(pflag)
  1698.                 doprnt(CurLn, CurLn);
  1699.         }
  1700.     }
  1701.     return (( nchngd == 0 && !gflg ) ? SUB_FAIL : nchngd);
  1702. }
  1703.  
  1704.  
  1705. /*    system.c    */
  1706. #ifndef __TURBOC__
  1707.  
  1708. #define SHELL    "/bin/sh"
  1709.  
  1710. system(c)
  1711. char *c; {
  1712.     int pid, status;
  1713.  
  1714.     switch (pid = fork()) {
  1715.     case -1:
  1716.         return -1;
  1717.     case 0:
  1718.         execl(SHELL, "sh", "-c", c, (char *) 0);
  1719.         exit(-1);
  1720.     default:
  1721.         while (wait(&status) != pid)
  1722.             ;
  1723.     }
  1724.     return status;
  1725. }
  1726. #endif  /* ifndef __TURBOC__ */
  1727.  
  1728.  
  1729. /*  docmd.c
  1730.  *    Perform the command specified in the input buffer, as pointed to
  1731.  *    by inptr.  Actually, this finds the command letter first.
  1732.  */
  1733.  
  1734. int docmd(glob)
  1735. int    glob;
  1736. {
  1737.     static char    rhs[MAXPAT];
  1738.     TOKEN    *subpat;
  1739.     int    c, err, line3;
  1740.     int    apflg, pflag, gflag;
  1741.     int    nchng;
  1742.     char    *fptr;
  1743.  
  1744.     pflag = FALSE;
  1745.     Skip_White_Space;
  1746.  
  1747.     c = *inptr++;
  1748.     switch(c) {
  1749.     case NL:
  1750.         if( nlines == 0 && (Line2 = nextln(CurLn)) == 0 )
  1751.             return(ERR);
  1752.         CurLn = Line2;
  1753.         return (1);
  1754.  
  1755.     case '=':
  1756.         printf("%d\n",Line2);
  1757.         break;
  1758.  
  1759.     case 'a':
  1760.         if(*inptr != NL || nlines > 1)
  1761.             return(ERR);
  1762.  
  1763.         if(append(Line1, glob) < 0)
  1764.             return(ERR);
  1765.         fchanged = TRUE;
  1766.         break;
  1767.  
  1768.     case 'c':
  1769.         if(*inptr != NL)
  1770.             return(ERR);
  1771.  
  1772.         if(deflt(CurLn, CurLn) < 0)
  1773.             return(ERR);
  1774.  
  1775.         if(del(Line1, Line2) < 0)
  1776.             return(ERR);
  1777.         if(append(CurLn, glob) < 0)
  1778.             return(ERR);
  1779.         fchanged = TRUE;
  1780.         break;
  1781.  
  1782.     case 'd':
  1783.         if(*inptr != NL)
  1784.             return(ERR);
  1785.  
  1786.         if(deflt(CurLn, CurLn) < 0)
  1787.             return(ERR);
  1788.  
  1789.         if(del(Line1, Line2) < 0)
  1790.             return(ERR);
  1791.         if(nextln(CurLn) != 0)
  1792.             CurLn = nextln(CurLn);
  1793.         fchanged = TRUE;
  1794.         break;
  1795.  
  1796.     case 'e':
  1797.         if(nlines > 0)
  1798.             return(ERR);
  1799.         if(fchanged)
  1800.             return CHANGED;
  1801.         /*FALL THROUGH*/
  1802.     case 'E':
  1803.         if(nlines > 0)
  1804.             return(ERR);
  1805.  
  1806.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1807.             return(ERR);
  1808.  
  1809.         if((fptr = getfn()) == NULL)
  1810.             return(ERR);
  1811.  
  1812.         clrbuf();
  1813.         if((err = doread(0, fptr)) < 0)
  1814.             return(err);
  1815.  
  1816.         strcpy(fname, fptr);
  1817.         fchanged = FALSE;
  1818.         break;
  1819.  
  1820.     case 'f':
  1821.         if(nlines > 0)
  1822.             return(ERR);
  1823.  
  1824.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1825.             return(ERR);
  1826.  
  1827.         if((fptr = getfn()) == NULL)
  1828.             return(ERR);
  1829.  
  1830.         if (nofname)
  1831.             printf("%s\n", fname);
  1832.         else
  1833.             strcpy(fname, fptr);
  1834.         break;
  1835.  
  1836.     case 'i':
  1837.         if(*inptr != NL || nlines > 1)
  1838.             return(ERR);
  1839.  
  1840.         if(append(prevln(Line1), glob) < 0)
  1841.             return(ERR);
  1842.         fchanged = TRUE;
  1843.         break;
  1844.  
  1845.     case 'j':
  1846.         if (*inptr != NL || deflt(CurLn, CurLn+1)<0)
  1847.             return(ERR);
  1848.  
  1849.         if (join(Line1, Line2) < 0)
  1850.             return(ERR);
  1851.         break;
  1852.  
  1853.     case 'k':
  1854.         Skip_White_Space;
  1855.  
  1856.         if (*inptr < 'a' || *inptr > 'z')
  1857.             return ERR;
  1858.         c= *inptr++;
  1859.  
  1860.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1861.             return(ERR);
  1862.  
  1863.         mark[c-'a'] = Line1;
  1864.         break;
  1865.  
  1866.     case 'l':
  1867.         if(*inptr != NL)
  1868.             return(ERR);
  1869.         if(deflt(CurLn,CurLn) < 0)
  1870.             return(ERR);
  1871.         if (dolst(Line1,Line2) < 0)
  1872.             return(ERR);
  1873.         break;
  1874.  
  1875.     case 'm':
  1876.         if((line3 = getone()) < 0)
  1877.             return(ERR);
  1878.         if(deflt(CurLn,CurLn) < 0)
  1879.             return(ERR);
  1880.         if(move(line3) < 0)
  1881.             return(ERR);
  1882.         fchanged = TRUE;
  1883.         break;
  1884.  
  1885.     case 'P':
  1886.     case 'p':
  1887.         if(*inptr != NL)
  1888.             return(ERR);
  1889.         if(deflt(CurLn,CurLn) < 0)
  1890.             return(ERR);
  1891.         if(doprnt(Line1,Line2) < 0)
  1892.             return(ERR);
  1893.         break;
  1894.  
  1895.     case 'q':
  1896.         if(fchanged)
  1897.             return CHANGED;
  1898.         /*FALL THROUGH*/
  1899.     case 'Q':
  1900.         if(*inptr == NL && nlines == 0 && !glob)
  1901.             return(EOF);
  1902.         else
  1903.             return(ERR);
  1904.  
  1905.     case 'r':
  1906.         if(nlines > 1)
  1907.             return(ERR);
  1908.  
  1909.         if(nlines == 0)            /* The original code tested */
  1910.             Line2 = LastLn;        /*    if(nlines = 0)        */
  1911.                         /* which looks wrong.  RAM  */
  1912.  
  1913.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1914.             return(ERR);
  1915.  
  1916.         if((fptr = getfn()) == NULL)
  1917.             return(ERR);
  1918.  
  1919.         if((err = doread(Line2, fptr)) < 0)
  1920.             return(err);
  1921.         fchanged = TRUE;
  1922.         break;
  1923.  
  1924.     case 's':
  1925.         if(*inptr == 'e')
  1926.             return(set());
  1927.         Skip_White_Space;
  1928.         if((subpat = optpat()) == NULL)
  1929.             return(ERR);
  1930.         if((gflag = getrhs(rhs)) < 0)
  1931.             return(ERR);
  1932.         if(*inptr == 'p')
  1933.             pflag++;
  1934.         if(deflt(CurLn, CurLn) < 0)
  1935.             return(ERR);
  1936.         if((nchng = subst(subpat, rhs, gflag, pflag)) < 0)
  1937.             return(ERR);
  1938.         if(nchng)
  1939.             fchanged = TRUE;
  1940.         break;
  1941.  
  1942.     case 't':
  1943.         if((line3 = getone()) < 0)
  1944.             return(ERR);
  1945.         if(deflt(CurLn,CurLn) < 0)
  1946.             return(ERR);
  1947.         if(transfer(line3) < 0)
  1948.             return(ERR);
  1949.         fchanged = TRUE;
  1950.         break;
  1951.  
  1952.     case 'W':
  1953.     case 'w':
  1954.         apflg = (c=='W');
  1955.  
  1956.         if(*inptr != ' ' && *inptr != HT && *inptr != NL)
  1957.             return(ERR);
  1958.  
  1959.         if((fptr = getfn()) == NULL)
  1960.             return(ERR);
  1961.  
  1962.         if(deflt(1, LastLn) < 0)
  1963.             return(ERR);
  1964.         if(dowrite(Line1, Line2, fptr, apflg) < 0)
  1965.             return(ERR);
  1966.         fchanged = FALSE;
  1967.         break;
  1968.  
  1969.     case 'x':
  1970.         if(*inptr == NL && nlines == 0 && !glob) {
  1971.             if((fptr = getfn()) == NULL)
  1972.                 return(ERR);
  1973.             if(dowrite(1, LastLn, fptr, 0) >= 0)
  1974.                 return(EOF);
  1975.         }
  1976.         return(ERR);
  1977.  
  1978.     case 'z':
  1979.         if(deflt(CurLn,CurLn) < 0)
  1980.             return(ERR);
  1981.  
  1982.         switch(*inptr) {
  1983.         case '-':
  1984.             if(doprnt(Line1-21,Line1) < 0)
  1985.                 return(ERR);
  1986.             break;
  1987.  
  1988.         case '.':
  1989.             if(doprnt(Line1-11,Line1+10) < 0)
  1990.                 return(ERR);
  1991.             break;
  1992.  
  1993.         case '+':
  1994.         case '\n':
  1995.             if(doprnt(Line1,Line1+21) < 0)
  1996.                 return(ERR);
  1997.             break;
  1998.         }
  1999.         break;
  2000.  
  2001.     default:
  2002.         return(ERR);
  2003.     }
  2004.     return (0);
  2005. }  /* docmd */
  2006.  
  2007.  
  2008. /*    doglob.c    */
  2009. doglob()
  2010. {
  2011.     int    lin, stat;
  2012.     char    *cmd;
  2013.     LINE    *ptr;
  2014.  
  2015.     cmd = inptr;
  2016.  
  2017.     for (;;) {
  2018.         for (lin=1; lin<=LastLn; lin++) {
  2019.             ptr = getptr(lin);
  2020.             if (ptr->l_stat & LGLOB)
  2021.                 break;
  2022.         }
  2023.         if (lin > LastLn)
  2024.             break;
  2025.  
  2026.         ptr->l_stat &= ~LGLOB;
  2027.         CurLn = lin;
  2028.         inptr = cmd;
  2029.         if((stat = getlst()) < 0)
  2030.             return(stat);
  2031.         if((stat = docmd(1)) < 0)
  2032.             return(stat);
  2033.     }
  2034.     return(CurLn);
  2035. }  /* doglob */
  2036.  
  2037.  
  2038. /*
  2039.  *  Software signal 2 or SIGINT is caught and the result is to resume
  2040.  *  the main loop at a command prompt.
  2041.  */
  2042. #include <setjmp.h>
  2043. jmp_buf    env;
  2044.  
  2045. #ifndef __TURBOC__
  2046. intr()
  2047. {
  2048.     puts("intr()?");
  2049.     longjmp(env, 1);
  2050. }
  2051.  
  2052. #else
  2053.  
  2054. void Catcher(void)
  2055. {
  2056.     longjmp(env, 1);
  2057. }
  2058. #endif    /* !__TURBOC__ */
  2059.  
  2060.  
  2061. /*--------  main  ---------------------------------------------------------*/
  2062.  
  2063. void cdecl main(argc,argv)
  2064. int    argc;
  2065. char    **argv;
  2066. {
  2067.     int    stat, i, doflush;
  2068.  
  2069.     set_ed_buf();
  2070.     doflush=isatty(1);
  2071.  
  2072.     if (argc>1 && argv[1][0]=='-' && argv[1][1]==0) {
  2073.         diag = 0;
  2074.         argc--;
  2075.         argv++;
  2076.     }
  2077.     if(argc > 1) {
  2078.         for(i = 1; i < argc; i++) {
  2079.             if(doread(0,argv[i])==0) {
  2080.                 CurLn = 1;
  2081.                 strcpy(fname, argv[i]);
  2082.                 break;
  2083.             }
  2084.         }
  2085.     }
  2086.  
  2087.     for (;;) {
  2088.  
  2089.         setjmp(env);
  2090.         putchar(':');        /*  The command-line prompt  */
  2091.  
  2092. #ifndef __TURBOC__
  2093.         signal(2, intr);
  2094. #else    /* __TURBOC__ */
  2095.         signal(SIGINT, Catcher);
  2096. #endif    /* !__TURBOC__ */
  2097.  
  2098.         if (doflush)
  2099.             fflush(stdout);
  2100.         if (fgets(inlin, sizeof(inlin),stdin) == NULL) {
  2101.             puts("Null input.");
  2102.             break;
  2103.         }
  2104.         if(*inlin == '!') {
  2105.             for(inptr = inlin; *inptr != NL; inptr++)
  2106.                 ;
  2107.             *inptr = EOS;
  2108.             system(inlin+1);
  2109.             continue;
  2110.         }
  2111.         inptr = inlin;
  2112.         if(getlst() >= 0)
  2113.             if((stat = ckglob()) != 0) {
  2114.                 if(stat >= 0 && (stat = doglob()) >= 0) {
  2115.                     CurLn = stat;
  2116.                     continue;
  2117.                 }
  2118.             } else {
  2119.                 if((stat = docmd(0)) >= 0) {
  2120.                     if(stat == 1)
  2121.                         doprnt(CurLn, CurLn);
  2122.                     continue;
  2123.                 }
  2124.             }
  2125.         switch (stat) {
  2126.         case EOF:
  2127.             _cleanup(); exit(0);
  2128.         case FATAL:
  2129.             fputs("FATAL ERROR\n",stderr);
  2130.             _cleanup(); exit(1);
  2131.         case CHANGED:
  2132.             puts("File has been changed.");
  2133.             break;
  2134.         case SET_FAIL:
  2135.             puts("`set' command failed.");
  2136.             break;
  2137.         case SUB_FAIL:
  2138.             puts("string substitution failed.");
  2139.             break;
  2140.         default:
  2141.             puts("Oops?");
  2142.             /*  Unrecognized or failed command (this  */
  2143.             /*  is SOOOO much better than "?" :-)      */
  2144.         }
  2145.     }
  2146. }  /* main */
  2147. /*________  end of source code  ____________________________________________*/
  2148.  
  2149.  
  2150. /*
  2151.  * Article 1981 of comp.os.minix:
  2152.  * From: ast@cs.vu.nl (Andy Tanenbaum)
  2153.  * Subject: Debugged version of V7 ed (replaces old one)
  2154.  * Message-ID: <1840@botter.cs.vu.nl>
  2155.  * Date: 31 Jan 88 16:40:06 GMT
  2156.  * Organization: VU Informatica, Amsterdam
  2157.  * Lines: 2365
  2158.  *
  2159.  * * * * * * * * * * * *
  2160.  *
  2161.  * Article 2015 of comp.os.minix:
  2162.  * From: ast@cs.vu.nl (Andy Tanenbaum)
  2163.  * Subject: Fix to ed.c I posted recently
  2164.  * Message-ID: <1841@botter.cs.vu.nl>
  2165.  * Date: 2 Feb 88 20:51:36 GMT
  2166.  * Organization: VU Informatica, Amsterdam
  2167.  * Lines: 4
  2168.  *
  2169.  * 2035a2036
  2170.  * >     LastLn -= Line2-Line1+1;
  2171.  * 2046a2048
  2172.  * >     LastLn += Line2-Line1+1;
  2173.  *
  2174.  * * * * * * * * * * * *
  2175.  *
  2176.  * Article 2053 of comp.os.minix:
  2177.  * From: dean@ndmath.UUCP (Dean Alvis)
  2178.  * Subject: ed.c tuneup
  2179.  * Keywords: minor adjustments
  2180.  * Message-ID: <930@ndmath.UUCP>
  2181.  * Date: 17 Feb 88 17:22:54 GMT
  2182.  * Organization: Math. Dept., Univ. of Notre Dame
  2183.  *
  2184.  *     I've enjoyed using the recently posted "ed", both on a UNIX
  2185.  * system and at home under CP/M. One minor annoyance is the slowness
  2186.  * of the write command for large (>>1000 lines) files. One solution
  2187.  * is to replace the calls to gettxt() (which in turn call getptr())
  2188.  * in dowrite(). I've lost the original, so can't provide diffs (sorry),
  2189.  * but the idea is to just use a LINE * in the loop in dowrite(), as
  2190.  * follows:
  2191.  *
  2192.  *     LINE *lptr;
  2193.  *     ...
  2194.  *     lptr = getptr(from);
  2195.  *     for(lin=from;lin<=to;lin++) {
  2196.  *         str = lptr->l_buff;
  2197.  *         ++lines;
  2198.  *         bytes += strlen(str)+1;    /* str + '\n' */
  2199.  *         if(fputs(str,fp) == EOF) {
  2200.  *             ...
  2201.  *         }
  2202.  *         fputc('\n',fp);
  2203.  *         lptr = lptr->l_next;
  2204.  *     }
  2205.  *
  2206.  * I've tested this version, and haven't run into trouble yet.
  2207.  * Also, this method avoids the strcpy and strcat to the static buffer
  2208.  * in gettxt(), which seem unnecessary (someone correct me if i'm
  2209.  * missing something here).
  2210.  *
  2211.  *     Another minor annoyance is the state of the current line
  2212.  * number after global commands - my preference is for the last modified
  2213.  * line to be the new current line. Replacing the return(0) by
  2214.  * return(CurLn) at the end of doglob() will produce this behavior.
  2215.  *
  2216.  *     dean
  2217.  * --
  2218.  * dean@ndmath.UUCP
  2219.  * Dean Alvis, Math. Dept., Univ. of Notre Dame, Notre Dame IN, 46556
  2220.  *
  2221.  *
  2222.  */
  2223.